package com.starsea.book.model

import com.starsea.book.constant.AppConst
import com.starsea.book.constant.AppLog
import com.starsea.book.data.appDb
import com.starsea.book.data.entities.*
import com.starsea.book.help.AppWebDav
import com.starsea.book.help.book.BookHelp
import com.starsea.book.help.book.ContentProcessor
import com.starsea.book.help.book.isLocal
import com.starsea.book.help.config.AppConfig
import com.starsea.book.help.config.ReadBookConfig
import com.starsea.book.help.coroutine.Coroutine
import com.starsea.book.help.http.newCallResponseBody
import com.starsea.book.help.http.okHttpClient
import com.starsea.book.help.http.text
import com.starsea.book.model.localBook.TextFile
import com.starsea.book.model.webBook.WebBook
import com.starsea.book.service.BaseReadAloudService
import com.starsea.book.ui.book.read.page.entities.TextChapter
import com.starsea.book.ui.book.read.page.provider.ChapterProvider
import com.starsea.book.ui.newpage.entity.BaseEntity
import com.starsea.book.ui.newpage.entity.BookInfoEntity
import com.starsea.book.ui.newpage.utils.gsonNormal
import com.starsea.book.utils.LogUtils
import com.starsea.book.utils.stackTraceStr
import com.starsea.book.utils.toastOnUi
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import okhttp3.RequestBody.Companion.toRequestBody
import splitties.init.appCtx
import kotlin.math.min


@Suppress("MemberVisibilityCanBePrivate")
object ReadBook : CoroutineScope by MainScope() {
    var book: Book? = null
    var callBack: CallBack? = null
    var inBookshelf = false
    var tocChanged = false
    var chapterSize = 0
    var durChapterIndex = 0
    var durChapterPos = 0
    var isLocalBook = true
    var chapterChanged = false
    var prevTextChapter: TextChapter? = null
    var curTextChapter: TextChapter? = null
    var nextTextChapter: TextChapter? = null
    var bookSource: BookSource? = null
    var msg: String? = null
    private val loadingChapters = arrayListOf<Int>()
    private val readRecord = ReadRecord()
    var readStartTime: Long = System.currentTimeMillis()

    /* 跳转进度前进度记录 */
    var lastBookPress: BookProgress? = null

    /* web端阅读进度记录 */
    var webBookProgress: BookProgress? = null

    var preDownloadTask: Coroutine<*>? = null
    val downloadedChapters = hashSetOf<Int>()
    val downloadFailChapters = hashMapOf<Int, Int>()
    var contentProcessor: ContentProcessor? = null
    val downloadScope = CoroutineScope(SupervisorJob() + IO)

    //暂时保存跳转前进度
    fun saveCurrentBookProcess() {
        if (lastBookPress != null) return //避免进度条连续跳转不能覆盖最初的进度记录
        lastBookPress = book?.let { BookProgress(it) }
    }

    //恢复跳转前进度
    fun restoreLastBookProcess() {
        lastBookPress?.let {
            setProgress(it)
            lastBookPress = null
        }
    }

    fun resetData(book: Book) {
        ReadBook.book = book
        readRecord.bookName = book.name
        readRecord.readTime = appDb.readRecordDao.getReadTime(book.name) ?: 0
        chapterSize = appDb.bookChapterDao.getChapterCount(book.bookUrl)
        contentProcessor = ContentProcessor.get(book)
        durChapterIndex = book.durChapterIndex
        durChapterPos = book.durChapterPos
        isLocalBook = book.isLocal
        clearTextChapter()
        callBack?.upMenuView()
        callBack?.upPageAnim()
        upWebBook(book)
        lastBookPress = null
        webBookProgress = null
        TextFile.txtBuffer = null
        synchronized(this) {
            loadingChapters.clear()
        }
    }

    fun upData(book: Book) {
        ReadBook.book = book
        chapterSize = appDb.bookChapterDao.getChapterCount(book.bookUrl)
        if (durChapterIndex != book.durChapterIndex || tocChanged) {
            durChapterIndex = book.durChapterIndex
            durChapterPos = book.durChapterPos
            clearTextChapter()
        }
        callBack?.upMenuView()
        upWebBook(book)
    }

    fun upWebBook(book: Book) {
        if (book.isLocal) {
            bookSource = null
        } else {
            appDb.bookSourceDao.getBookSource(book.origin)?.let {
                bookSource = it
//                if (book.getImageStyle().isNullOrBlank()) {
//                    book.setImageStyle(it.getContentRule().imageStyle)
//                }
            } ?: let {
                bookSource = null
            }
        }
    }

    fun setProgress(progress: BookProgress) {
        if (progress.durChapterIndex < chapterSize &&
            (durChapterIndex != progress.durChapterIndex
                    || durChapterPos != progress.durChapterPos)
        ) {
            durChapterIndex = progress.durChapterIndex
            durChapterPos = progress.durChapterPos
            clearTextChapter()
            callBack?.upContent()
            loadContent(resetPageOffset = true)
        }
    }

    fun clearTextChapter() {
        prevTextChapter = null
        curTextChapter = null
        nextTextChapter = null
    }

    fun uploadProgress() {
        book?.let {
            Coroutine.async {
                AppWebDav.uploadBookProgress(it)
                it.save()
            }
        }
    }

    fun upReadTime() {
        Coroutine.async(executeContext = IO) {
            readRecord.readTime = readRecord.readTime + System.currentTimeMillis() - readStartTime
            readStartTime = System.currentTimeMillis()
            readRecord.lastRead = System.currentTimeMillis()
            if (AppConfig.enableReadRecord) {
                appDb.readRecordDao.insert(readRecord)
            }
        }
    }

    fun upMsg(msg: String?) {
        if (ReadBook.msg != msg) {
            ReadBook.msg = msg
            callBack?.upContent()
        }
    }

    fun moveToNextPage(): Boolean {
        var hasNextPage = false
        curTextChapter?.let {
            val nextPagePos = it.getNextPageLength(durChapterPos)
            if (nextPagePos >= 0) {
                hasNextPage = true
                durChapterPos = nextPagePos
                callBack?.upContent()
                saveRead()
            }
        }
        return hasNextPage
    }

    fun moveToPrevPage(): Boolean {
        var hasPrevPage = false
        curTextChapter?.let {
            val prevPagePos = it.getPrevPageLength(durChapterPos)
            if (prevPagePos >= 0) {
                hasPrevPage = true
                durChapterPos = prevPagePos
                callBack?.upContent()
                saveRead()
            }
        }
        return hasPrevPage
    }

    fun moveToNextChapter(upContent: Boolean): Boolean {
        if (durChapterIndex < chapterSize - 1) {
            durChapterPos = 0
            durChapterIndex++
            prevTextChapter = curTextChapter
            curTextChapter = nextTextChapter
            nextTextChapter = null
            LogUtils.e(">>> ", "durChapterIndex = " + durChapterIndex)
            if (curTextChapter == null) {
                LogUtils.e(">>> ", "moveToNextChapter-章节未加载,开始加载 = ")
                AppLog.putDebug("moveToNextChapter-章节未加载,开始加载")
                callBack?.upContent()
                loadContent(durChapterIndex, upContent, resetPageOffset = false)
            } else if (upContent) {
                LogUtils.e(">>> ", "moveToNextChapter-章节已加载,刷新视图")
                AppLog.putDebug("moveToNextChapter-章节已加载,刷新视图")
                callBack?.upContent()
            }
            val index = durChapterIndex.plus(1)
            LogUtils.e(">>> ", "durChapterIndex = " + index)
            loadContent(index, upContent, false)
//            loadContent(durChapterIndex.plus(2), upContent, false)
//            loadContent(durChapterIndex.plus(3), upContent, false)
            saveRead()
            callBack?.upMenuView()
            AppLog.putDebug("moveToNextChapter-curPageChanged()")
            curPageChanged()
            return true
        } else {
            AppLog.putDebug("跳转下一章失败,没有下一章")
            return false
        }
    }

    fun moveToPrevChapter(
        upContent: Boolean,
        toLast: Boolean = true
    ): Boolean {
        if (durChapterIndex > 0) {
            durChapterPos = if (toLast) prevTextChapter?.lastReadLength ?: Int.MAX_VALUE else 0
            durChapterIndex--
            nextTextChapter = curTextChapter
            curTextChapter = prevTextChapter
            prevTextChapter = null
            if (curTextChapter == null) {
                callBack?.upContent()
                loadContent(durChapterIndex, upContent, resetPageOffset = false)
            } else if (upContent) {
                callBack?.upContent()
            }
            loadContent(durChapterIndex.minus(1), upContent, false)
            saveRead()
            callBack?.upMenuView()
            curPageChanged()
            return true
        } else {
            return false
        }
    }

    fun skipToPage(index: Int, success: (() -> Unit)? = null) {
        durChapterPos = curTextChapter?.getReadLength(index) ?: index
        callBack?.upContent {
            success?.invoke()
        }
        curPageChanged()
        saveRead()
    }

    fun setPageIndex(index: Int) {
        durChapterPos = curTextChapter?.getReadLength(index) ?: index
        saveRead()
        curPageChanged(true)
    }

    /**
     * 当前页面变化
     */
    private fun curPageChanged(pauseReadAloud: Boolean = false) {
        callBack?.pageChanged()
        if (BaseReadAloudService.isRun) {
            val scrollPageAnim = pageAnim() == 3
            if (scrollPageAnim && pauseReadAloud) {
                ReadAloud.pause(appCtx)
            } else {
                readAloud(!BaseReadAloudService.pause)
            }
        }
        upReadTime()
        preDownload()
    }

    /**
     * 朗读
     */
    fun readAloud(play: Boolean = true, startPos: Int = 0) {
        book?.let {
            ReadAloud.play(appCtx, play, startPos = startPos)
        }
    }

    /**
     * 当前页数
     */
    val durPageIndex: Int
        get() {
            return curTextChapter?.getPageIndexByCharIndex(durChapterPos) ?: durChapterPos
        }

    /**
     * chapterOnDur: 0为当前页,1为下一页,-1为上一页
     */
    fun textChapter(chapterOnDur: Int = 0): TextChapter? {
        return when (chapterOnDur) {
            0 -> curTextChapter
            1 -> nextTextChapter
            -1 -> prevTextChapter
            else -> null
        }
    }

    /**
     * 加载当前章节和前后一章内容
     * @param resetPageOffset 滚动阅读是否重置滚动位置
     * @param success 当前章节加载完成回调
     */
    fun loadContent(
        resetPageOffset: Boolean,
        success: (() -> Unit)? = null
    ) {
        loadContent(durChapterIndex, resetPageOffset = resetPageOffset) {
            success?.invoke()
        }
        loadContent(durChapterIndex + 1, resetPageOffset = resetPageOffset)
        loadContent(durChapterIndex - 1, resetPageOffset = resetPageOffset)
    }

    /**
     * 加载章节内容
     * @param index 章节序号
     * @param upContent 是否更新视图
     * @param resetPageOffset 滚动阅读是否重置滚动位置
     * @param success 加载完成回调
     */
    fun loadContent(
        index: Int,
        upContent: Boolean = true,
        resetPageOffset: Boolean = false,
        success: (() -> Unit)? = null
    ) {

        LogUtils.e(">>>>> ", "index = $index")
        val addLoading = addLoading(index)
        LogUtils.e(">>>>> ", "addLoading = " + addLoading)
        if (addLoading) {
            LogUtils.e(">>>> ", "1111111loadContent book = ")
            Coroutine.async {

                LogUtils.e(">>>> ", "loadContent book = ")

                val book = book!!

                LogUtils.e(">>>> ", "loadContent book = " + book.toString())

                appDb.bookChapterDao.getChapter(book.bookUrl, index)?.let { chapter ->

                    LogUtils.e(">>>> ", "chapter = " + chapter.toString())
                    if (BookHelp.hasContent(book, chapter)) {
                        BookHelp.getContent(book, chapter)?.let {
                            LogUtils.e(">>>> ", "getContent = " + it.toString())
                            contentLoadFinish(
                                book,
                                chapter,
                                it,
                                upContent,
                                resetPageOffset
                            ) {
                                LogUtils.e(">>>> ", "getContent = " + it.toString())
                                success?.invoke()
                            }
                        } ?: download(
                            downloadScope,
                            chapter,
                            resetPageOffset
                        )
                    } else
                        if (book.isLocal) {
                            BookHelp.getContent(book, chapter)?.let {
                                LogUtils.e(">>>> ", "getContent = " + it.toString())
                                contentLoadFinish(
                                    book,
                                    chapter,
                                    it,
                                    upContent,
                                    resetPageOffset
                                ) {
                                    LogUtils.e(">>>> ", "getContent = " + it.toString())
                                    success?.invoke()
                                }
                            } ?: download(
                                downloadScope,
                                chapter,
                                resetPageOffset
                            )
                        } else {
                            okHttpClient.newCallResponseBody {
                                url(AppConst.BASE_URL.plus("front-api/book/book_section_detail?sectionId=${chapter.url}"))
                                    .post("".toRequestBody())
                            }.text().let {
                                com.blankj.utilcode.util.LogUtils.e(">>>> " + it)
                                val entity: BaseEntity<BookInfoEntity> =
                                    it.gsonNormal(BookInfoEntity::class.java)
//                        dataBannerResult.postValue(entity)
                                LogUtils.e(">>>> ", "content = " + entity.data!!.content)
                                contentLoadFinish(
                                    book,
                                    chapter,
                                    entity.data!!.content ?: "",
                                    upContent,
                                    resetPageOffset
                                ) {
                                    success?.invoke()
                                }
                            }
                        }

//                    BookHelp.getContent(book, chapter)?.let {
//
//                        LogUtils.e(">>>> ", "getContent = " + it.toString())
//                        contentLoadFinish(
//                            book,
//                            chapter,
//                            it,
//                            upContent,
//                            resetPageOffset
//                        ) {
//
//                            LogUtils.e(">>>> ", "getContent = " + it.toString())
//                            success?.invoke()
//                        }
//                    } ?: download(
//                        downloadScope,
//                        chapter,
//                        resetPageOffset
//                    )
                } ?: removeLoading(index)
            }.onError {
                LogUtils.e(">>>> ", "加载正文出错\n${it.localizedMessage}")
                removeLoading(index)
                LogUtils.e(">>>> ", "加载正文出错\n${it.localizedMessage}")
                AppLog.put("加载正文出错\n${it.localizedMessage}")
            }
        }
    }

    /**
     * 下载正文
     */
    private suspend fun downloadIndex(index: Int) {
        LogUtils.e("downloadIndex", "downloadIndex = index = " + index)
        LogUtils.e("downloadIndex", "downloadIndex = chapterSize = " + chapterSize)

        if (index < 0) return
        LogUtils.e("downloadIndex", "downloadIndex = index = " + (index > chapterSize - 1))
        if (index > chapterSize - 1) {
            upToc()
            return
        }
        LogUtils.e("downloadIndex", "downloadIndex = book = " + book?.toString())
        val book = book ?: return

//        if (addLoading(index)) {
//            try {
//                appDb.bookChapterDao.getChapter(book.bookUrl, index)?.let { chapter ->
//
//                    LogUtils.e("downloadIndex", "downloadIndex chapter = " + chapter.toString())
//                    if (BookHelp.hasContent(book, chapter)) {
//                        LogUtils.e("downloadIndex", "downloadIndex chapter = hasContent")
//                        removeLoading(chapter.index)
//                        downloadedChapters.add(chapter.index)
//                    } else {
//                        delay(1000)
//                        LogUtils.e("downloadIndex", "downloadIndex chapter 没有下载")
//                        download(downloadScope, chapter, false)
//                    }
//                } ?: removeLoading(index)
//            } catch (e: Exception) {
//                removeLoading(index)
//            }
//        }
    }

    /**
     * 下载正文
     */
    private fun download(
        scope: CoroutineScope,
        chapter: BookChapter,
        resetPageOffset: Boolean,
        success: (() -> Unit)? = null
    ) {
        val book = book ?: return removeLoading(chapter.index)
        val bookSource = bookSource

        LogUtils.e("download", "download = bookSource = " + bookSource.toString())

        if (bookSource != null) {
            CacheBook.getOrCreate(bookSource, book).download(scope, chapter)
        } else {
//            val msg = if (book.isLocal) "无内容" else "没有书源"
//            contentLoadFinish(
//                book,
//                chapter,
//                "加载正文失败\n$msg",
//                resetPageOffset = resetPageOffset,
//            ) {
//                success?.invoke()
//            }
        }
    }

    private fun addLoading(index: Int): Boolean {
        synchronized(this) {
            if (loadingChapters.contains(index)) return false
            loadingChapters.add(index)
            return true
        }
    }

    fun removeLoading(index: Int) {
        synchronized(this) {
            loadingChapters.remove(index)
        }
    }

    /**
     * 内容加载完成
     */
    fun contentLoadFinish(
        book: Book,
        chapter: BookChapter,
        content: String,
        upContent: Boolean = true,
        resetPageOffset: Boolean,
        success: (() -> Unit)? = null
    ) {

        LogUtils.e(">>>> ", "contentLoadFinish = " + chapter.index)
        removeLoading(chapter.index)
        if (chapter.index !in durChapterIndex - 1..durChapterIndex + 1) {
            return
        }
        Coroutine.async {
            val contentProcessor = ContentProcessor.get(book.name, book.origin)
            val displayTitle = chapter.getDisplayTitle(
                contentProcessor.getTitleReplaceRules(),
                book.getUseReplaceRule()
            )
            val contents = contentProcessor
                .getContent(book, chapter, content, includeTitle = false)
            val textChapter = ChapterProvider
                .getTextChapter(book, chapter, displayTitle, contents, chapterSize)
            when (val offset = chapter.index - durChapterIndex) {
                0 -> {
                    curTextChapter = textChapter
                    if (upContent) callBack?.upContent(offset, resetPageOffset)
                    callBack?.upMenuView()
                    curPageChanged()
                    callBack?.contentLoadFinish()
                }

                -1 -> {
                    prevTextChapter = textChapter
                    if (upContent) callBack?.upContent(offset, resetPageOffset)
                }

                1 -> {
                    nextTextChapter = textChapter
                    if (upContent) callBack?.upContent(offset, resetPageOffset)
                }
            }
            Unit
        }.onError {
            AppLog.put("ChapterProvider ERROR", it)
            appCtx.toastOnUi("ChapterProvider ERROR:\n${it.stackTraceStr}")
        }.onSuccess {
            success?.invoke()
        }
    }

    @Synchronized
    fun upToc() {

        LogUtils.e(">>>>> ", ">  upToc ")
        val bookSource = bookSource ?: return
        val book = book ?: return
        if (System.currentTimeMillis() - book.lastCheckTime < 600000) return
        book.lastCheckTime = System.currentTimeMillis()

        LogUtils.e(">>>>> ", ">asjfajsifalskdasdja ")

        WebBook.getChapterList(this, bookSource, book).onSuccess(IO) { cList ->
            if (book.bookUrl == ReadBook.book?.bookUrl
                && cList.size > chapterSize
            ) {
                appDb.bookChapterDao.insert(*cList.toTypedArray())
                chapterSize = cList.size
                nextTextChapter ?: loadContent(durChapterIndex + 1)
            }
        }
    }

    fun pageAnim(): Int {
        return book?.getPageAnim() ?: ReadBookConfig.pageAnim
    }

    fun setCharset(charset: String) {
        book?.let {
            it.charset = charset
            callBack?.loadChapterList(it)
        }
        saveRead()
    }

    fun saveRead() {
        Coroutine.async(executeContext = IO) {
            val book = book ?: return@async
            book.lastCheckCount = 0
            book.durChapterTime = System.currentTimeMillis()
            val chapterChanged = book.durChapterIndex != durChapterIndex
            book.durChapterIndex = durChapterIndex
            book.durChapterPos = durChapterPos
            if (chapterChanged) {
                appDb.bookChapterDao.getChapter(book.bookUrl, durChapterIndex)?.let {
                    book.durChapterTitle = it.getDisplayTitle(
                        ContentProcessor.get(book.name, book.origin).getTitleReplaceRules()
                    )
                }
            }
            appDb.bookDao.update(book)
        }
    }

    /**
     * 预下载
     */
    private fun preDownload() {

        LogUtils.e("preDownload", "preDownload = " + book?.isLocal)
        if (book?.isLocal == true) return
        LogUtils.e("preDownload", "preDownload = preDownloadNum = " + AppConfig.preDownloadNum)
        if (AppConfig.preDownloadNum < 2) {
            return
        }
        preDownloadTask?.cancel()
        preDownloadTask = Coroutine.async(executeContext = IO) {
            //预下载
            launch {
                val maxChapterIndex = min(durChapterIndex + AppConfig.preDownloadNum, chapterSize)
                for (i in durChapterIndex.plus(2)..maxChapterIndex) {
                    if (downloadedChapters.contains(i)) continue
                    if ((downloadFailChapters[i] ?: 0) >= 3) continue
                    downloadIndex(i)
                }
            }
            launch {
                val minChapterIndex = durChapterIndex - min(5, AppConfig.preDownloadNum)
                for (i in durChapterIndex.minus(2) downTo minChapterIndex) {
                    if (downloadedChapters.contains(i)) continue
                    if ((downloadFailChapters[i] ?: 0) >= 3) continue
                    downloadIndex(i)
                }
            }
        }
    }

    interface CallBack {
        fun upMenuView()

        fun loadChapterList(book: Book)

        fun upContent(
            relativePosition: Int = 0,
            resetPageOffset: Boolean = true,
            success: (() -> Unit)? = null
        )

        fun pageChanged()

        fun contentLoadFinish()

        fun upPageAnim()

        fun notifyBookChanged()
    }

}
